package com.leenmvc.core.utils.validator;

import com.leenmvc.core.annotation.PrimaryKey;
import com.leenmvc.core.annotation.validator.*;
import com.leenmvc.core.aop.proxy.ProxyUtil;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.dao.communal.BaseDao;
import com.leenmvc.core.dao.communal.Wrapper;
import com.leenmvc.core.dao.mysql.wrapper.CountWrapper;
import com.leenmvc.core.exception.ValidatorException;
import com.leenmvc.core.utils.ClassUtil;
import com.leenmvc.core.utils.ConvertUtils;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.TipUtil;
import com.leenmvc.core.utils.collection.MapUtils;
import com.leenmvc.core.utils.spring.SpringContextUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 校验工具类
 *
 * @author leen
 * @since 1.0.0
 */
public class ValidatorUtils {

    private static BaseDao baseDao = ProxyUtil.getProxyDao()[0];

    private static ResourceBundleMessageSource getMessageSource() {
        ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
        bundleMessageSource.setDefaultEncoding("UTF-8");
        bundleMessageSource.setBasenames("i18n/validation");
        return bundleMessageSource;
    }

    /**
     * 这个方法用于校验传入对象和实体注解是否吻合
     * 主要验证传入实体
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     */
    public static void validateEntity(Object object, Class<?>... groups)
            throws ValidatorException {
        ValidatorUtils.notNull("请检查被校验的对象是否为空！", object);
        Locale.setDefault(LocaleContextHolder.getLocale());
        Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
                new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
                .buildValidatorFactory().getValidator();

        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            ConstraintViolation<Object> constraint = constraintViolations.iterator().next();
            throw new ValidatorException(constraint.getMessage());
        }
        validatUniqueness(object, groups);
        validatForegin(object, groups);
        validatExist(object, groups);
        validatValueIn(object, groups);
        validatePatterValid(object,groups);
    }

    /**
     * 验证输入值是否在给定的范围内
     *
     * @param object
     * @param groups
     */
    private static void validatValueIn(Object object, Class<?>[] groups) {
        List<Field> valueInFields = ClassUtil.getValueInFields(object.getClass());
        for (Field field : valueInFields) {
            Object fieldValue = ClassUtil.getFieldValue(object, field.getName());
            ValueIn valueIn = field.getAnnotation(ValueIn.class);
            if (checkGroups(valueIn.groups(), groups)) {
                if (!ArrayUtils.contains(valueIn.value(), String.valueOf(fieldValue))) {
                    throw new ValidatorException(valueIn.message());
                }
                ;
            }
        }
    }

    /**
     * 单个字段的删除验证
     *
     * @param field
     * @param primaryFieldValue
     * @param groups
     */
    private static void validSingleExistField(Field field, Object primaryFieldValue, Class<?>[] groups) {
        Exist annotation = field.getAnnotation(Exist.class);
        PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
        if (annotation != null && primaryKey != null) {
            Class<?>[] exists = annotation.classes();
            if (!checkGroups(annotation.groups(), groups)) {
                return;
            }
            String[] fields = annotation.fields();
            for (int i = 0; i < fields.length; i++) {
                String name = exists[i].getSimpleName();
                try {
                    Object o = exists[i].newInstance();
                    Method count = exists[i].getMethod("count", Wrapper.class);
                    Class<?> classType = getClassType(o);
                    CountWrapper countWrapper = BaseEntity.getDialect(classType).getWrapperFactory().getCountWrapper(classType);
                    countWrapper.eq(fields[i], primaryFieldValue);
                    Integer invoke = (Integer) count.invoke(o, countWrapper);
                    if (invoke > 0) {
                        throw new ValidatorException("在其他表中存在将要删除记录的引用，请检查！" + name);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 验证是否在其他数据表中存在记录
     *
     * @param object
     * @param groups
     */
    private static void validatExist(Object object, Class<?>[] groups) {
        Class<?> cls = object.getClass();
        Field field = ClassUtil.getPrimaryField(cls);
        Exist annotation = field.getAnnotation(Exist.class);
        if (annotation != null && annotation.del()) {
            Class<?>[] exists = annotation.classes();
            Class<?>[] groups1 = annotation.groups();
            Object primaryFieldValue = ClassUtil.getPrimaryFieldValue(object);
            // 根据groups检测是否需要进行验证
            boolean checkIt = false;
            for (Class<?> c : groups) {
                checkIt = ArrayUtils.contains(groups1, c);
                if (checkIt) break;
            }
            if (!checkIt || primaryFieldValue == null) return;

            String[] fields = annotation.fields();
            for (int i = 0; i < fields.length; i++) {
                String name = exists[i].getSimpleName();
                try {
                    Object o = exists[i].newInstance();
                    Method count = exists[i].getMethod("count", Wrapper.class);
                    Class<?> classType = getClassType(o);
                    CountWrapper countWrapper = BaseEntity.getDialect(classType).getWrapperFactory().getCountWrapper(classType);
                    countWrapper.eq(fields[i], primaryFieldValue);
                    Integer invoke = (Integer) count.invoke(o, countWrapper);
                    if (invoke > 0) {// 关联删除中间表记录（只能删除关联中间表的记录，如果该中间表又作为其他表的中间表那么可能会发生问题）
                        Method delByWrapper = exists[i].getMethod("delByWrapper", Wrapper.class);
                        Wrapper deleteWrapper = BaseEntity.getDialect(classType).getWrapperFactory().getDeleteWrapper(classType);
                        deleteWrapper.eq(fields[i], primaryFieldValue);
                        Object invoke1 = delByWrapper.invoke(o, deleteWrapper);
//                      throw new ValidatorException(TipUtil.getTip("EXIST_VALID", name));
                        System.out.println(invoke);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 唯一性校验
     *
     * @param object
     * @param groups
     * @see Uniqueness
     */
    private static void validatUniqueness(Object object, Class<?>[] groups) {
        Class<?> cls = object.getClass();
        try {
            List<Field> uniquenessFields = ClassUtil.getUniquenessField(cls);
            for (Field field : uniquenessFields) {
                Method getMethod = ClassUtil.getGetMethod(field);
                Object uniqueFieldValue = getMethod.invoke(object);
                Uniqueness annotation = field.getAnnotation(Uniqueness.class);
                Class<?>[] groups1 = annotation.groups();

                // 根据groups检测是否需要进行验证
                boolean checkIt = false;
                for (Class<?> c : groups) {
                    checkIt = ArrayUtils.contains(groups1, c);
                    if (checkIt) break;
                }
                if (!checkIt || uniqueFieldValue == null) continue;

                String[] linkFields = annotation.linkFields();

                // 一个字段唯一确定
                Wrapper queryWrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
                queryWrapper.eq(field.getName(), uniqueFieldValue);

                if (ClassUtil.getPrimaryFieldValue(object) != null) {
                    queryWrapper.uEq(BaseEntity.getPrimaryFieldName(cls), ClassUtil.getPrimaryFieldValue(object));
                }

                // 多个字段唯一确定
                switch (linkFields.length) {
                    case 1:
                        Object fieldValue = ClassUtil.getFieldValue(object, linkFields[0]);
                        BaseEntity.getDialect(cls).getWrapperFactory().closeWrapper(queryWrapper);
                        queryWrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
                        queryWrapper.eq(field.getName(), uniqueFieldValue)
                                .eq(linkFields[0], fieldValue)
                                .uEq(BaseEntity.getPrimaryFieldName(cls), ClassUtil.getPrimaryFieldValue(object));
                        break;
                    case 2:
                        Object fieldValue1 = ClassUtil.getFieldValue(object, linkFields[0]);
                        Object fieldValue2 = ClassUtil.getFieldValue(object, linkFields[1]);
                        BaseEntity.getDialect(cls).getWrapperFactory().closeWrapper(queryWrapper);
                        queryWrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
                        queryWrapper.eq(field.getName(), uniqueFieldValue)
                                .eq(linkFields[0], fieldValue1)
                                .eq(linkFields[1], fieldValue2)
                                .uEq(BaseEntity.getPrimaryFieldName(cls), ClassUtil.getPrimaryFieldValue(object));
                        break;
                    default:

                }

                if (baseDao.count(queryWrapper) > 0) {
                    throw new ValidatorException(TipUtil.getTip(annotation.message(), field.getName()));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证正则
     * */
    public static void validatePatterValid(Object object, Class<?>... groups) {
        Class<?> cls = object.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            PatternValid annotation = field.getAnnotation(PatternValid.class);
            if (annotation != null) {
                if (checkGroups(annotation.groups(), groups)) {
                    String fieldName = field.getName();
                    String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    try {
                        Method method = cls.getMethod(getMethodName);
                        String fieldValue = method.invoke(object).toString();
                        Pattern patterns = Pattern.compile(annotation.value());
                        Matcher matcher = patterns.matcher("" + fieldValue);
                        if (!matcher.find()) {
                            throw new ValidatorException(annotation.message());
                        }
                    } catch (IllegalAccessException e){
                        e.printStackTrace();
                    } catch (InvocationTargetException e){
                        e.printStackTrace();
                    } catch (NoSuchMethodException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 验证单个字段(根据主键更新)
     *
     * @param field      验证字段
     * @param fieldValue Map，字段KV对，其中包含了主键
     * @param groups     组
     */
    public static void validateSingleField(Field field, Object fieldValue, Class<?>... groups) {
        Annotation[] annotations = field.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof NotNull) { //
                NotNull notNull = field.getAnnotation(NotNull.class);
                if (checkGroups(notNull.groups(), groups)) {
                    notNull(notNull.message(), fieldValue);
                }
                continue;
            }

            if (annotation instanceof PatternValid) {
                PatternValid pattern = field.getAnnotation(PatternValid.class);
                if (checkGroups(pattern.groups(), groups)) {
                    Pattern patterns = Pattern.compile(pattern.value());
                    Matcher matcher = patterns.matcher("" + fieldValue);
                    if (!matcher.find()) {
                        throw new ValidatorException("与给定的规则不相匹配，不予执行！");
                    }
                }
                continue;
            }

            if (annotation instanceof ValueIn) { //
                ValueIn valueIn = field.getAnnotation(ValueIn.class);
                if (checkGroups(valueIn.groups(), groups)) {
                    if (!ArrayUtils.contains(valueIn.value(), String.valueOf(fieldValue))) {
                        throw new ValidatorException(valueIn.message());
                    }
                    ;
                }
                continue;
            }

            if (annotation.equals(NotEmpty.class)) { //
                NotEmpty annotation1 = field.getAnnotation(NotEmpty.class);
                if (checkGroups(annotation1.groups(), groups)) {
                    if (StringUtils.isEmpty(fieldValue)) {
                        throw new ValidatorException(annotation1.message());
                    }
                }
                continue;
            }

            if (annotation instanceof Foreign) {// 检测外键
                validSingleForeignField(field, fieldValue, groups);
                continue;
            }

            if (annotation instanceof Exist) {// 检测外键
                validSingleExistField(field, fieldValue, groups);
                continue;
            }
        }
    }

    /**
     * 这个方法是为了能够使用 Uniqueness 注解
     *
     * @param field
     * @param params
     * @param groups
     */
    public static void validateSingleField(Field field, Map<String, Object> params, Class<?>... groups) {
        Object fieldValue = params.get(field.getName());
        validateSingleField(field, fieldValue, groups);
        Uniqueness annotation = field.getAnnotation(Uniqueness.class);
        if (annotation != null) {
            validUniqSingleField(field, params, groups);
        }
    }

    /**
     * 是否需要进行本注解的验证工作
     *
     * @param annoGroups  注解包含的组
     * @param validGroups 将要验证的组
     * @return
     */
    private static boolean checkGroups(Class<?>[] annoGroups, Class<?>[] validGroups) {
//        Class<?>[] groups1 = foreign.groups();
        boolean checkIt = false;
        for (Class<?> c : validGroups) {
            checkIt = ArrayUtils.contains(annoGroups, c);
            if (checkIt) {
                return true;
            }
            ;
        }
        return checkIt;
    }

    /**
     * 验证单个字段的值对应的外键是否存在
     *
     * @param field
     * @param value
     * @param groups
     */
    private static void validSingleForeignField(Field field, Object value, Class<?>[] groups) {
        Foreign foreign = field.getAnnotation(Foreign.class);
        if (!checkGroups(foreign.groups(), groups)) return;
        if (value == null) {
            throw new ValidatorException("验证不通过，禁止空值！！");
        }
        ;
        Class<?> value1 = foreign.value();
        try {
            Object o = value1.newInstance();
            Class<?> classType = getClassType(o);
            Method count = value1.getMethod("count", Wrapper.class);
            Wrapper wrapper = BaseEntity.getDialect(classType).getWrapperFactory().getCountWrapper(classType);
            wrapper.eq(BaseEntity.getPrimaryFieldName(classType), value);
            Integer invoke = (Integer) count.invoke(o, wrapper);
            if (invoke == 0) {
                throw new ValidatorException(TipUtil.getTip(foreign.message(), value));
            }
            return;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证单个字段的唯一性
     *
     * @param field
     * @param params Map，字段KV对，其中包含了主键
     */
    private static void validUniqSingleField(Field field, Map<String, Object> params, Class<?>... groups) {
        Object value = params.get(field.getName());
        Class<?> cls = field.getDeclaringClass();
        Object primaryValue = params.get(BaseEntity.getPrimaryFieldName(cls));
        Uniqueness uniqueness = field.getAnnotation(Uniqueness.class);
        if (!checkGroups(uniqueness.groups(), groups)) return;
        if (value == null) {
            throw new ValidatorException("验证不通过，禁止空值！！");
        }

        String simpleName = cls.getSimpleName();
        String s = StringUtils.lowercaseFirstChar(simpleName) + "Dao";
        Object baseDao = SpringContextUtils.getBean(s);
        Class<?> aClass = baseDao.getClass();
        String[] linkFields = uniqueness.linkFields();

        try {
            Method oneFieldMethod = null;
            oneFieldMethod = aClass.getMethod("getOneField", Wrapper.class);
            Wrapper wrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
            wrapper.eq(field.getName(), value).uEq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
            switch (linkFields.length) {
                case 1:
                    Object fieldValue = params.get(linkFields[0]);
                    if (fieldValue == null) {
                        Wrapper eq = BaseEntity.getDialect(cls).getWrapperFactory().getQueryWrapper(cls);
                        eq.incField(linkFields[0]).eq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
                        fieldValue = oneFieldMethod.invoke(baseDao, eq);
                    }

                    wrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
                    wrapper.eq(field.getName(), value)
                            .eq(linkFields[0], fieldValue)
                            .uEq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
                    break;
                case 2:
                    Object fieldValue1 = params.get(linkFields[0]);
                    if (fieldValue1 == null) {
                        Wrapper eq = BaseEntity.getDialect(cls).getWrapperFactory().getQueryWrapper(cls);
                        eq.incField(linkFields[0]).eq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
                        fieldValue1 = oneFieldMethod.invoke(baseDao, eq);
                    }
                    Object fieldValue2 = params.get(linkFields[1]);
                    if (fieldValue2 == null) {
                        Wrapper eq = BaseEntity.getDialect(cls).getWrapperFactory().getQueryWrapper(cls);
                        eq.incField(linkFields[1]).eq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
                        fieldValue2 = oneFieldMethod.invoke(baseDao, eq);
                    }
                    wrapper = BaseEntity.getDialect(cls).getWrapperFactory().getCountWrapper(cls);
                    wrapper.eq(field.getName(), value)
                            .eq(linkFields[0], fieldValue1)
                            .eq(linkFields[1], fieldValue2)
                            .uEq(BaseEntity.getPrimaryFieldName(cls), primaryValue);
                    break;
                default:
            }

            Method countMethod = aClass.getMethod("count", Wrapper.class);
            Integer count = (Integer) countMethod.invoke(baseDao, wrapper);
            if (count > 0) {
                throw new ValidatorException(uniqueness.message());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验外键是否存在记录
     *
     * @param object
     */
    private static void validatForegin(Object object, Class<?>... groups) {
        // 校验当前的对象中外键字段是否存在与之对应的记录
        Class<?> cls = object.getClass();
        try {
            List<Field> foreignFields = ClassUtil.getForeignField(cls);
            for (Field field : foreignFields) {
                Method getMethod = ClassUtil.getGetMethod(field);
                Object foreginId = getMethod.invoke(object);
                Foreign annotation = field.getAnnotation(Foreign.class);
                // 根据groups检测是否需要进行验证
                if (!checkGroups(annotation.groups(), groups)) continue;
                if (foreginId == null) continue;
                Class<?> value = annotation.value();
                Object o1 = value.newInstance();
                Method count = value.getMethod("get", Object.class);

                Object o = count.invoke(o1, foreginId.toString());
                if (o == null) {
                    throw new ValidatorException(TipUtil.getTip(annotation.message(), foreginId));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }


    /**
     * 检验对象是否为空
     *
     * @param message
     * @param obj
     */
    public static void notNull(String message, Object... obj) {
        for (Object o : obj) {
            if (StringUtils.isEmpty(o)) {
                throw new ValidatorException(message);
            }
        }
    }

    /**
     * 验证是否为空数组
     *
     * @param message
     * @param array
     */
    public static void isArrayEmpty(String message, Object[] array) {
        if (array == null) {
            throw new ValidatorException(message);
        }
        if (array.length == 0) {
            throw new ValidatorException(message);
        }
    }

    /**
     * 验证是否为空列表
     *
     * @param message
     * @param list
     */
    public static <T> void isListEmpty(String message, List<T> list) {
        if (list == null) {
            throw new ValidatorException(message);
        }
        if (list.size() == 0) {
            throw new ValidatorException(message);
        }
    }

    /**
     * 验证是否可以转换成数字
     *
     * @param message
     * @param o
     */
    public static <T> T convertToNum(String message, Object o, Class<?> cls) {
        if (StringUtils.isEmpty(o) || !StringUtils.isNumericZidai(o)) {
            throw new ValidatorException(message);
        }
        try {
            return (T) ConvertUtils.convertType(o, cls);
        } catch (NumberFormatException n) {
            throw new ValidatorException(message);
        }
    }

    ;

    /**
     * 验证是否可以转换成指定的类型
     *
     * @param message
     * @param o
     */
    public static <T> T convertToTargetType(String message, Object o, Class<?> cls) {
        if (o == null) {
            throw new ValidatorException(message);
        }
        try {
            return (T) ConvertUtils.convertType(o, cls);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        throw new ValidatorException(message);
    }

    ;


    /**
     * 验证是否为空map
     *
     * @param message
     * @param map
     */
    public static void isMapEmpty(String message, Map map) {
        if (map == null) {
            throw new ValidatorException(message);
        }
        if (MapUtils.isEmpty(map)) {
            throw new ValidatorException(message);
        }
    }

    /**
     * 验证两个字符串是否相等
     *
     * @param message
     * @param password
     * @param dbpassword
     */
    public static void equals(String message, String password, String dbpassword) {
        if (!password.equals(dbpassword)) {
            throw new ValidatorException(message);
        }
    }

    /**
     * 获取父类型的泛型信息
     *
     * @return
     */
    private static Class<?> getClassType(Object obj) {
        Type type = obj.getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) type;
        Type[] ts = pt.getActualTypeArguments();
        Class c = (Class) ts[0];
        return c;
    }
}
